package com.net.xpay.common.service.impl;

import com.net.xpay.common.domain.ApiMonitor;
import com.net.xpay.common.manager.ApiMonitorManager;
import com.net.xpay.common.model.monitor.AddApiMonitorDataModel;
import com.net.xpay.common.model.monitor.ApiMonitorModel;
import com.net.common.util.NetUtil;
import com.net.common.util.SerializationUtil;
import com.net.xpay.common.service.ApiMonitorService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 接口调用监控
 */
@Slf4j
@Service
public class ApiMonitorServiceImpl implements ApiMonitorService {

    @Autowired
    private ApiMonitorManager apiMonitorManager;

    /**
     * 内网ip
     */
    private final String serverIp = NetUtil.getHostIp();

    /**
     * 监控数据
     */
    private volatile ConcurrentHashMap<String/*api*/, ApiMonitorModel> apiMonitorMap1 = new ConcurrentHashMap<>();
    private volatile ConcurrentHashMap<String/*api*/, ApiMonitorModel> apiMonitorMap2 = new ConcurrentHashMap<>();

    /**
     * 异常指标
     */
    private volatile ConcurrentHashMap<String/*api*/, AtomicLong/*异常数*/> apiErrorMap1 = new ConcurrentHashMap<>();
    private volatile ConcurrentHashMap<String/*api*/, AtomicLong/*异常数*/> apiErrorMap2 = new ConcurrentHashMap<>();

    private final AtomicBoolean index = new AtomicBoolean(true);

    private ConcurrentHashMap<String/*api*/, ApiMonitorModel> getApiMonitorMap() {
        if (index.get()) {
            return apiMonitorMap1;
        } else {
            return apiMonitorMap2;
        }
    }


    @Override
    public void addMonitorData(AddApiMonitorDataModel model) {
        ApiMonitorModel monitorModel = getApiMonitorMap().get(model.getApi());
        //把model里的数据维护到apiMonitorMap
        if (monitorModel == null) {
            initApiMonitorData(model);
        } else {
            updateApiMonitorData(monitorModel, model);
        }
    }

    @Override
    public void addErrorData(String api) {
        //把异常数据维护到apiMonitorMap
        AtomicLong errorCount = getApiErrorMap().get(api);
        if (errorCount == null) {
            initErrApiMonitorData(api);
        } else {
            errorCount.incrementAndGet();
        }
    }

    @Override
    public void flushMonitorData() {
        //切换存放监控数据的索引
        ConcurrentHashMap<String, ApiMonitorModel> apiMonitorMap = getApiMonitorMap();
        ConcurrentHashMap<String/*api*/, AtomicLong/*异常数*/> apiErrorMap = getApiErrorMap();
        boolean prevIndex = index.get();
        index.set(!index.get());

        if (prevIndex) {
            apiMonitorMap1 = new ConcurrentHashMap<>();
            apiErrorMap1 = new ConcurrentHashMap<>();
        } else {
            apiMonitorMap2 = new ConcurrentHashMap<>();
            apiErrorMap2 = new ConcurrentHashMap<>();
        }

        LocalDateTime now = LocalDateTime.now();
        LocalDate day = now.toLocalDate();
        int hour = now.getHour();
        log.info("flushMonitorData apiMonitorMap={}", SerializationUtil.obj2String(apiMonitorMap));

        for(Map.Entry<String, ApiMonitorModel> entry : apiMonitorMap.entrySet()){
            ApiMonitor apiMonitor = new ApiMonitor();
            ApiMonitorModel model = entry.getValue();
            apiMonitor.setTotalReqCostTime(model.getTotalReqCostTime().longValue());
            apiMonitor.setMinReqCostTime(model.getMinReqCostTime().longValue());
            apiMonitor.setMaxReqCostTime(model.getMaxReqCostTime().longValue());
            apiMonitor.setReqCount(model.getReqCount().longValue());
            apiMonitor.setApiName(model.getApiName());
            String api = entry.getKey();
            apiMonitor.setApi(api);

            AtomicLong errReqCount = apiErrorMap.get(api);
            if (errReqCount == null){
                apiMonitor.setErrReqCount(0L);
            } else {
                apiMonitor.setErrReqCount(errReqCount.longValue());
            }

            apiMonitor.setIp(serverIp);
            apiMonitor.setDay(day);
            apiMonitor.setHour(hour);

            apiMonitorManager.save(apiMonitor);
        }
    }

    private synchronized void initErrApiMonitorData(String api) {
        AtomicLong errorCount = getApiErrorMap().get(api);
        if (errorCount == null) {
            getApiErrorMap().put(api, new AtomicLong(1L));
        } else {
            errorCount.incrementAndGet();
        }
    }

    private synchronized void initApiMonitorData(AddApiMonitorDataModel model) {
        ApiMonitorModel monitorModel = getApiMonitorMap().get(model.getApi());
        if (monitorModel == null) {
            //初始化
            monitorModel = new ApiMonitorModel();
            monitorModel.setApiName(model.getApiName());
            monitorModel.setReqCount(new AtomicLong(1L));
            monitorModel.setTotalReqCostTime(new AtomicLong(model.getReqCostTime()));
            monitorModel.setMaxReqCostTime(new AtomicLong(model.getReqCostTime()));
            monitorModel.setMinReqCostTime(new AtomicLong(model.getReqCostTime()));
            getApiMonitorMap().put(model.getApi(), monitorModel);
        } else {
            updateApiMonitorData(monitorModel, model);
        }
    }


    /**
     * 更新监控数据
     */
    private void updateApiMonitorData(ApiMonitorModel updateMonitorModel, AddApiMonitorDataModel model) {
        updateMonitorModel.getReqCount().incrementAndGet();
        updateMonitorModel.getTotalReqCostTime().addAndGet(model.getReqCostTime());

        updateMonitorModel.getMaxReqCostTime().getAndAccumulate(model.getReqCostTime(), Math::max);
        updateMonitorModel.getMinReqCostTime().getAndAccumulate(model.getReqCostTime(), Math::min);
    }

    private ConcurrentHashMap<String/*api*/, AtomicLong/*异常数*/> getApiErrorMap() {
        if (index.get()) {
            return apiErrorMap1;
        } else {
            return apiErrorMap2;
        }
    }

}
